﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json.Linq;
using OF.JsonRpc.Client.RpcInvokers;

namespace OF.JsonRpc.Client
{
    /// <summary>
    /// 注入Rpc上下文的委托
    /// </summary>
    /// <param name="collection"></param>
    public delegate void GlobalContextSetHandler(IDictionary<string, object> collection);

    /// <summary>
    /// Rpc客户端访问类。
    ///     Call    常用
    ///     T Call<T> 常用
    ///     void CallWithOption
    ///     T CallWithOption<T>
    ///     T CallUrl<T>
    ///     T CallWithDeclaredParams<T>
    /// </summary>
    public static class Rpc
    {
        internal static GlobalContextSetHandler GlobalContextSet;

        /// <summary>
        /// 添加上下文触发事件，当调用JSON-RPC服务之前触发，从而注入上下文键值。
        /// </summary>
        /// <param name="handler"></param>
        public static void AddGlobalContextSetHandler(GlobalContextSetHandler handler)
        {
            GlobalContextSet += handler;
        }

        /// <summary>
        /// 调用JSON-RPC服务（服务地址需要配置在App.config中的Setting下的JsonRpcServiceUrl的Key值）。
        /// </summary>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <returns></returns>
        public static void Call(string method, params object[] args)
        {
            Call<object>(method, args);
        }

        /// <summary>
        /// Call方法的异步调用
        /// </summary>
        /// <param name="successedAction">执行成功后调用的函数</param>
        /// <param name="failedAction">执行失败后调用的函数</param>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        public static void CallAsync(Action successedAction,
            Action<Exception> failedAction,
            string method, params object[] args)
        {
            Action<object> successedActionFinal = null;
            if (successedAction != null)
                successedActionFinal = o => successedAction();

            CallAsync<object>(successedActionFinal, failedAction, method, args);
        }

        /// <summary>
        /// 调用JSON-RPC服务（服务地址需要配置在App.config中的Setting下的JsonRpcServiceUrl的Key值）。
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <returns></returns>
        public static T Call<T>(string method, params object[] args)
        {
            var rpcInvoker = GetRpcInvoker(method, null);
            var jresp = rpcInvoker.Invoke<T>(method, args);
            if (jresp.Error != null)
                throw jresp.Error;

            return jresp.Result;
        }

        private static void ProcessCallAsync<T>(object result)
        {
            var worker = new BackgroundWorker();
            worker.DoWork += ( /*object*/ sender, /*DoWorkEventArgs*/ e) =>
            {
                if (result is CallAsyncResult<T> ar)
                {
                    try
                    {
                        var rpcInvoker = GetRpcInvoker(ar.Method, null);
                        JsonResponse<T> jresp;
                        if (ar.IsDeclaredParams)
                            jresp = rpcInvoker.InvokeWithDeclaredParams<T>(ar.Method, ar.DeclaredArgs);
                        else
                            jresp = rpcInvoker.Invoke<T>(ar.Method, ar.Args);
                        if (jresp.Error != null)
                        {
                            ar.Error = jresp.Error;
                        }

                        ar.Result = jresp.Result;
                    }
                    catch (Exception ex)
                    {
                        ar.Error = ex;
                    }
                    ar.SetCompleted();
                }
            };
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Call方法的异步调用
        /// </summary>
        /// <param name="successedAction">执行成功后调用的函数</param>
        /// <param name="failedAction">执行失败后调用的函数</param>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        public static void CallAsync<T>(Action<T> successedAction,
            Action<Exception> failedAction,
            string method, params object[] args)
        {
            var state = new CallAsyncResult<T>
            {
                Method = method,
                Args = args,
                SuccessedCallBack = successedAction,
                FailedCallBack = failedAction
            };
            //ProcessCallAsync<T>(state);
            ThreadPool.QueueUserWorkItem(ProcessCallAsync<T>, state);
        }

        /// <summary>
        /// 调用JSON-RPC服务（服务地址需要配置在App.config中的Setting下的JsonRpcServiceUrl的Key值）。
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="option">自定义呼叫s选项</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <returns></returns>
        public static void CallWithOption(string method, RpcOption option, params object[] args)
        {
            CallWithOption<object>(method, option, args);
        }

        /// <summary>
        /// 调用JSON-RPC服务（服务地址需要配置在App.config中的Setting下的JsonRpcServiceUrl的Key值）。
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="option">自定义呼叫s选项</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <returns></returns>
        public static T CallWithOption<T>(string method, RpcOption option, params object[] args)
        {
            var rpcInvoker = GetRpcInvoker(method, option);

            var jresp = rpcInvoker.Invoke<T>(method, args);
            if (jresp.Error != null)
                throw jresp.Error;

            return jresp.Result;
        }

        /// <summary>
        /// 调用JSON-RPC服务。
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="url">JSON-RPC服务地址</param>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为可变数组</param>
        /// <returns></returns>
        public static T CallUrl<T>(string url, string method, params object[] args)
        {
            var rpcInvoker = GetRpcInvoker(method, new RpcOption {ServiceAddress = url});

            JsonResponse<T> jresp = rpcInvoker.Invoke<T>(method, args);

            if (jresp.Error != null)
                throw jresp.Error;

            return jresp.Result;
        }

        /// <summary>
        /// 调用JSON-RPC服务（服务地址需要配置在App.config中的Setting下的JsonRpcServiceUrl的Key值）。
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为将参数数组对象化的写法，如new { param1 = "A", param2 = "B"  }</param>
        /// <returns></returns>
        public static T CallWithDeclaredParams<T>(string method, object args)
        {
            var rpcInvoker = GetRpcInvoker(method, null);
            JsonResponse<T> jresp = rpcInvoker.InvokeWithDeclaredParams<T>(method, args);

            if (jresp.Error != null)
                throw jresp.Error;

            return jresp.Result;
        }

        /// <summary>
        /// CallWithDeclaredParams的异步调用方法
        /// </summary>
        /// <typeparam name="T">JSON-RPC方法返回的Result的对象类型。</typeparam>
        /// <param name="successedAction">执行成功后调用的函数</param>
        /// <param name="failedAction">执行失败后调用的函数</param>
        /// <param name="method">JSON-RPC方法名。</param>
        /// <param name="args">JSON-RPC方法接收的参数，此参数为将参数数组对象化的写法，如new { param1 = "A", param2 = "B"  }</param>
        public static void CallWithDeclaredParamsAsync<T>(Action<T> successedAction,
            Action<Exception> failedAction, string method, object args)
        {
            var state = new CallAsyncResult<T>
            {
                IsDeclaredParams = true,
                Method = method,
                DeclaredArgs = args,
                SuccessedCallBack = successedAction,
                FailedCallBack = failedAction
            };
            //ProcessCallAsync<T>(state);
            ThreadPool.QueueUserWorkItem(ProcessCallAsync<T>, state);
        }

        /// <summary>
        /// 批量调用JSON-RPC服务。
        /// </summary>
        /// <param name="jsonRpcList">批量JSON-RPC请求列表</param>
        /// <param name="option">参数列表</param>
        /// <returns></returns>
        public static List<JsonResponse> BatchCall(List<JsonRequest> jsonRpcList, RpcOption option = null)
        {
            if (jsonRpcList == null || jsonRpcList.Count == 0)
                throw new ArgumentNullException("jsonRpcList", "jsonRpcList no data");
            var rpcInvoker = GetRpcInvoker(jsonRpcList[0].Method, option);
            return rpcInvoker.BatchInvoke(jsonRpcList);
        }

        private static RpcInvoker GetRpcInvoker(string method, RpcOption option)
        {
            string serviceAddress = option != null && !string.IsNullOrWhiteSpace(option.ServiceAddress)
                ? option.ServiceAddress
                : GetServiceUrlFromConfig(method);

            RpcInvoker invoker;
            if (string.Equals(serviceAddress, "local", StringComparison.InvariantCultureIgnoreCase))
            {
                invoker = new LocalRpcInvoker();
            }
            else if (serviceAddress.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
            {
                invoker = new HttpRpcInvoker();
            }
            else
            {
                throw new NotSupportedException("不支持的Rpc服务地址类型:" + serviceAddress);
            }

            invoker.ServiceAddress = serviceAddress;
            invoker.Option = option;
            return invoker;
        }

        #region get service url from config

        private static string GetServiceUrlDomainFromConfig(string method)
        {
            var domain = ExtractDomainName(method);
            if (string.IsNullOrEmpty(domain))
                return null;
            return System.Configuration.ConfigurationManager.AppSettings["JsonRpcServiceUrl." + domain];
        }

        private static string ExtractDomainName(string method)
        {
            var elements = method.Split('.');
            return elements.Length >= 2 ? elements[0] : null;
        }

        private static string GetServiceUrlFromConfig(string method)
        {
            var serviceUrlByDomain = GetServiceUrlDomainFromConfig(method);
            string serviceUrl = System.Configuration.ConfigurationManager.AppSettings["JsonRpcServiceUrl"];
            if (!string.IsNullOrWhiteSpace(serviceUrlByDomain))
                serviceUrl = serviceUrlByDomain;
            if (string.IsNullOrWhiteSpace(serviceUrl))
                throw new Exception("Config setting JsonRpcServiceUrl is empty.");
            return serviceUrl;
        }

        #endregion
    }
}